/* typehints:start */
import { Application } from "../../application";
/* typehints:end */

import { sha1 } from "./utils";
import { compressX64, decompressX64 } from "./lzstring";
import { globalConfig } from "../../config/global";
import { createLogger } from "./logging";
import SaveGameWorker from '../../workers/savegame.worker'

export const compressionPrefix = String.fromCodePoint(1);

const logger = createLogger('read_write_proxy')

export class ReadWriteProxy{
  constructor(app, filename){
    /** @type {Application} */
    this.app = app;

    this.filename = filename;
    this.currentData = null;

    this.worker = new SaveGameWorker();
  }

  getDefaultData(){
    abstract
    return {};
  }

  workerToWrite(){
    this.worker.postMessage({
      data: this.currentData,
      isCompressed: G_IS_DEV && globalConfig.debug.compressData
    })
    return new Promise(resolve => {
      this.worker.addEventListener('message', ev => {
        this.writeAsync(true, ev.data.data).then(() => {
          resolve();
        })
      })
    })
  }

  writeAsync(noCompress = false, compressed){
    if(!noCompress){
      const jsonString = JSON.stringify(this.currentData);
  
      const checksum = sha1(jsonString);
      compressed = compressionPrefix + compressX64(checksum + jsonString);
  
      if((G_IS_DEV && !globalConfig.debug.compressData)){
        compressed = jsonString;
      }
    }

    return new Promise(resolve => {
      this.app.storage.writeFileAsync(this.filename, compressed).then(() => {
        logger.log('📄 写入', this.filename);
        resolve(compressed);
      })
    })
  }

  deleteAsync(){
    return this.app.storage
      .deleteFileAsync(this.filename)
      .then(() => {
        logger.log('📄 删除', this.filename);
      })
  }

  readAsync(){
    return this.app.storage
      .readFileAsync(this.filename)
      .then(data => {
        if(data == null){
          return JSON.stringify(this.getDefaultData());
        }

        if(data.startsWith(compressionPrefix)){
          const decompressed = decompressX64(data.substr(compressionPrefix.length));
          if(!decompressed){
            return Promise.reject('保存的数据不存在');
          }

          if(decompressed.length < 40){
            return Promise.reject('保存的数据长度不足40位');
          }

          const checksum = decompressed.substring(0, 40);
          const jsonString = decompressed.substr(40);

          const desiredChecksum = sha1(jsonString);
          if(desiredChecksum !== checksum){
            return Promise.reject('保存的数据被篡改');
          }

          return jsonString;
        }

        return data;
      })

      .then(data => {
        try{
          return JSON.parse(data)
        } catch(err){
          logger.error(
            '解析数据时错误',
            this.filename,
            ':',
            e,
            '错误数据(', data,')'
          )
          throw new Error('解析数据出错')
        }
      })

      .then(data => {
        this.currentData = data;
        logger.log('📄 读取', this.filename);
        return data;
      })

      .catch(err => {
        return Promise.reject('读取数据出错: ' + this.filename + ':' + err);
      })
  }
}